package com.charles.seckillPlatform.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.charles.seckillComponentCommon.constants.CacheConstants;
import com.charles.seckillComponentCommon.constants.ErrorConstants;
import com.charles.seckillComponentCommon.domain.AjaxResult;
import com.charles.seckillComponentRedis.service.RedisService;
import com.charles.seckillComponentSecurity.annotation.RolePermit;
import com.charles.seckillData.domain.SysGood;
import com.charles.seckillPlatform.mapper.SysGoodMapper;
import com.charles.seckillPlatform.service.SysGoodService;
import com.charles.seckillPlatform.vo.UserGoodId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/good")
public class SysGoodController {

    public static final Logger logger = LoggerFactory.getLogger(SysGoodController.class);

    @Autowired
    private SysGoodMapper sysGoodMapper;

    @Autowired
    private SysGoodService sysGoodService;

    @Autowired
    private RedisService redisService;

    /**
     * 产品列表
     */
    @GetMapping("goodList")
    @RolePermit(values = "admin:common")
    public AjaxResult queryGoodList() {
        return AjaxResult.success(sysGoodMapper.queryGoodList());
    }

    /**
     * 产品信息
     */
    @GetMapping("/getGood/{goodId}")
    @RolePermit(values = "admin:common")
    public AjaxResult getGood(@PathVariable("goodId") Long goodId) {
        SysGood good = (SysGood) redisService.getCacheObject(CacheConstants.GOOD_ID + goodId);
        if (good == null) {
            good = sysGoodMapper.getGood(goodId);
        }
        return AjaxResult.success(good);
    }

    /**
     * 生成秒杀产品的令牌
     */
    @GetMapping("/generateToken")
    @SentinelResource(value = "generateToken", blockHandler = "generateTokenBlockHandler", fallback = "generateFallback")
    @RolePermit(values = "admin:common")
    public AjaxResult generateToken(@RequestParam Long userId, @RequestParam Long goodId) {
        sysGoodService.requestCountAndVerify();
        return AjaxResult.success("成功", sysGoodService.generateSeckillToken(new UserGoodId(userId, goodId, StrUtil.EMPTY)));
    }

    /**
     * 令牌生成熔断
     *
     * @param userId 用户id
     * @param goodId 产品id
     * @param ex 抛出异常
     */
    public AjaxResult generateTokenBlockHandler(Long userId, Long goodId, BlockException ex) {
        logger.error("generateTokenBlockHandler: " + ex.getMessage());
        return AjaxResult.error(ErrorConstants.BLOCKING_ERROR);
    }

    /**
     * 令牌生成熔断
     *
     * @param userId 用户id
     * @param goodId 产品id
     * @param throwable 抛出异常
     */
    public AjaxResult generateFallback(Long userId, Long goodId, Throwable throwable) {
        logger.error("generateFallback: " + throwable.getMessage());
        return AjaxResult.error(throwable.getMessage());
    }

    /**
     * 秒杀产品
     */
    @PostMapping("seckill")
    @SentinelResource(value = "seckillGood", blockHandler = "seckillGoodBlockHandler", fallback = "seckillGoodFallback")
    @RolePermit(values = "admin:common")
    public AjaxResult seckill(@RequestBody UserGoodId userGoodId) {
        sysGoodService.requestCountAndVerify();
        sysGoodService.verifySeckillToken(userGoodId);
        return AjaxResult.success(sysGoodService.seckillGood(userGoodId.getUser_id(), userGoodId.getGood_id()));
    }

    /**
     * 限流
     *
     * @param userGoodId 用户产品id
     * @param ex 抛出异常
     */
    public AjaxResult seckillGoodBlockHandler(UserGoodId userGoodId, BlockException ex) {
        logger.error("seckillGoodBlockHandler: UserGoodId = " + userGoodId + ", Error = " + ex.getMessage());
        return AjaxResult.error(ErrorConstants.BLOCKING_ERROR);
    }

    /**
     * 熔断
     *
     * @param userGoodId 用户产品id
     * @param throwable 抛出异常
     */
    public AjaxResult seckillGoodFallback(UserGoodId userGoodId, Throwable throwable) {
        logger.error("seckillGoodBlockHandler: UserGoodId = " + userGoodId + ", Error = " + throwable.getMessage());
        return AjaxResult.error(throwable.getMessage());
    }

}
